package cn.antscrpt.antLib.test 
{
/*===================================================*//**
 * Box23DAS3 and Papervision3D Demo
 * Please drag stage balls
 * 
 * ボールをジョイントでつないでみました
 * 注：Line3Dがリークしていて時間経過で負荷が高まっていきます>_<
 * 
 * @author Yasu
 * @see http://clockmaker.jp/blog/
 * @since 2009.04.05
 *//*===================================================*/

    import Box2D.Collision.Shapes.*;
    import Box2D.Collision.b2AABB;
    import Box2D.Common.Math.b2Vec2;
    import Box2D.Dynamics.Joints.*;
    import Box2D.Dynamics.*;
    
    import flash.net.*;
    import flash.ui.*;
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    
    import org.papervision3d.events.*;
    import org.papervision3d.lights.*;
    import org.papervision3d.materials.shadematerials.*;
    import org.papervision3d.objects.*;
    import org.papervision3d.view.*;
    import org.papervision3d.objects.primitives.*;
    import org.papervision3d.core.geom.renderables.*;
    import org.papervision3d.core.geom.*;
    import org.papervision3d.materials.special.*;
    
    import net.hires.debug.Stats;
    
    [SWF(width="464", height="465", frameRate="30")]
    public class EX16 extends BasicView
    {
        // const vars
        static public const OBJ_SIZE:int = 40;
        static public const OBJ_NUM:uint = 10;
        static public const OBJ_COLOR:uint = 0x3399FF;
        
        // vars for Box2D
        private var worldWidth:Number;
        private var worldHeight:Number;
        private var m_iterations:int;
        private var m_wallWidth:Number;
        private var m_wallHeight:Number;
        private var m_timeStep:Number;
        private var m_physScale:Number;
        private var m_world:b2World;
        private var m_mouseJoint:b2MouseJoint;
        private var m_draggedBody:b2Body;
        private var mouseXWorldPhys:Number;
        private var mouseYWorldPhys:Number;
        private var isMouseDown:Boolean;
        private var arrayIndex:int;
        
        // array of objs
        private var pv3dObjsArr:Vector.<DisplayObject3D> = new Vector.<DisplayObject3D>(OBJ_NUM, true);
        private var box3dSpapesArr:Vector.<b2Body> = new Vector.<b2Body>(OBJ_NUM, true);
        private var joints:Vector.<b2DistanceJoint> = new Vector.<b2DistanceJoint>(OBJ_NUM, true);;
        private var line3D:Line3D;
        private var lines3D:Lines3D;
        private var lm:LineMaterial;
        
        /**
         * Constructor
         */
        public function EX16()
        {
            stage.quality = StageQuality.LOW;
            
            // init PV3D
            super(465, 465, false, true);
            
            // for poligon cross problem, but it's heavy...
            //renderer = new QuadrantRenderEngine(QuadrantRenderEngine.QUAD_SPLIT_FILTER);
            
            // init PV3D World
            createPaervision3dWorld();
            
            // init Box2D World
            createBox2dWorld()
            
            // init vars for drag
            arrayIndex = -1;
            isMouseDown = false;

            // addEvent
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            
            // Backgound
            createBackGround();
            cerateFullScreenBtn();
            
            // debug
            addChild(new Stats)
        }
        
        /**
         * Create Papervision3D World
         */
        private function createPaervision3dWorld():void
        {
            camera.zoom = 1000 / camera.focus + 1;
            camera.x = 0;
            camera.z = -1000;
            
            // create light
            var light:PointLight3D = new PointLight3D();
            light.z = -100;
            
            for(var i:int=0; i < OBJ_NUM; i++)
            {
                var mat:FlatShadeMaterial
                if(i == 0) mat = new FlatShadeMaterial(light, 0x990000);
                else mat = new FlatShadeMaterial(light, OBJ_COLOR);
                mat.interactive = true;
                
                var obj3d:DisplayObject3D = scene.addChild(new Sphere(mat, OBJ_SIZE/2, 6, 5));
                obj3d.extra = { radius:OBJ_SIZE, arrayPos:i };
                obj3d.x = - Math.random() * 200 + 100;
                obj3d.y = Math.random() * -200;
                obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, obj3dMousePressHandler);
                obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, obj3dMouseOverHandler);
                obj3d.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, obj3dMouseOutHandler);
                
                pv3dObjsArr[i] = obj3d;
            }
            
            // lines
            lines3D = new Lines3D();
            scene.addChild(lines3D);
            lm = new LineMaterial(0xFFFFFF);
        }
        
        /**
         * Create Box2D World
         */
        private function createBox2dWorld():void
        {
            // init Box2D
            worldWidth = stage.stageWidth;
            worldHeight = stage.stageHeight;
            m_iterations = 5;
            m_timeStep = 1 / stage.frameRate;
            m_physScale = 60;
            var worldAABB:b2AABB = new b2AABB();
            worldAABB.lowerBound.Set( -1000, -1000);
            worldAABB.upperBound.Set(1000, 1000);
            var gravity:b2Vec2 = new b2Vec2(0, 10);
            var doSleep:Boolean = true;
            m_world = new b2World(worldAABB, gravity, doSleep);

            // craete wall for Box2D
            var wallShapeDef:b2PolygonDef = new b2PolygonDef();
            var wallBodyDef:b2BodyDef = new b2BodyDef();
            var wall:b2Body;
            m_wallWidth = stage.stageWidth;
            m_wallHeight = stage.stageHeight;

            // left wall
            wallShapeDef.SetAsBox(10 / m_physScale, m_wallHeight / 2 / m_physScale);
            wallBodyDef.position.Set(0, m_wallHeight / 2 / m_physScale);
            wall = m_world.CreateBody(wallBodyDef);
            wall.CreateShape(wallShapeDef);

            // right wall
            wallBodyDef.position.Set(m_wallWidth / m_physScale, m_wallHeight / 2 / m_physScale);
            wall = m_world.CreateBody(wallBodyDef);
            wall.CreateShape(wallShapeDef);

            // upper wall
            wallShapeDef.SetAsBox(m_wallWidth / 2 / m_physScale, 10 / m_physScale);
            wallBodyDef.position.Set(m_wallWidth / 2 / m_physScale, 0);
            wall = m_world.CreateBody(wallBodyDef);
            wall.CreateShape(wallShapeDef);
            
            // bottom wall
            wallBodyDef.position.Set(m_wallWidth / 2 / m_physScale, m_wallHeight / m_physScale);
            wall = m_world.CreateBody(wallBodyDef);
            wall.CreateShape(wallShapeDef);
            
            wall.SetMassFromShapes();
            
            for(var i:int=0; i < OBJ_NUM; i++)
            {
                var obj3d:DisplayObject3D = pv3dObjsArr[i];
                
                var boxShape:b2CircleDef = new b2CircleDef();
                boxShape.radius = OBJ_SIZE / m_physScale / 2;
                boxShape.density = 3;
                boxShape.friction = 10;
                boxShape.restitution = 0.75;
                var bodyDef:b2BodyDef = new b2BodyDef();
                bodyDef.position.Set((obj3d.x + worldWidth / 2) / m_physScale, (obj3d.y + worldHeight / 2) / m_physScale);
                var body:b2Body = m_world.CreateBody(bodyDef);
                body.CreateShape(boxShape);
                body.SetUserData(obj3d);
                body.SetMassFromShapes();
                
                box3dSpapesArr[i] = body;
            }
            
            // create joints
            for (i = 1; i < box3dSpapesArr.length; i++)
            {
                // joint to balls from drag obj
                var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
                jointDef.Initialize(
                    box3dSpapesArr[0], 
                    box3dSpapesArr[i], 
                    box3dSpapesArr[0].GetPosition(), 
                    box3dSpapesArr[i].GetPosition());
                var joint:b2DistanceJoint = m_world.CreateJoint(jointDef) as b2DistanceJoint;
                joint.m_length = 1;
                joint.m_frequencyHz = 1.5;
                joint.m_dampingRatio = 0;
            }
        }
        
        
        /**
         * get mouse position, and convert box2d scale
         */
        private function updateMouseWorld():void
        {
            mouseXWorldPhys = mouseX / m_physScale;
            mouseYWorldPhys = mouseY / m_physScale;
        }

        /**
         * Enter Frame
         * @param    event
         */
        private function enterFrameHandler(event:Event):void
        {
            // update Box2D step
            updateMouseWorld();
            mouseDrag();
            m_world.Step(m_timeStep, m_iterations);

            // sync position to PV3D from Box2D
            for (var bb:b2Body = m_world.GetBodyList(); bb; bb = bb.GetNext())
            {
                if (bb.GetUserData()is DisplayObject3D)
                {
                    bb.GetUserData().x = bb.GetPosition().x * m_physScale - worldWidth / 2;
                    bb.GetUserData().y = -bb.GetPosition().y * m_physScale + worldHeight / 2;
                    bb.GetUserData().rotationZ = -bb.GetAngle() * (180 / Math.PI);
                }
            }
            
            // lines
            lines3D.removeAllLines();
            
            for (var i:int = 1; i < pv3dObjsArr.length; i++) 
            {
                var os:DisplayObject3D = pv3dObjsArr[0];
                var oe:DisplayObject3D = pv3dObjsArr[i];
                var startV:Vertex3D = new Vertex3D(os.x, os.y, os.z);
                var endV:Vertex3D = new Vertex3D(oe.x, oe.y, oe.z);
                var line:Line3D = new Line3D(lines3D, lm, 1, startV, endV);
                lines3D.addLine(line);
            }
            
            singleRender();
        }

        /**
         * Drag And Drop
         */
        private function mouseDrag():void
        {
            if (isMouseDown && ! m_mouseJoint)
            {
                m_draggedBody = null;
                    
                if (arrayIndex > -1)
                    m_draggedBody = box3dSpapesArr[arrayIndex];
                
                if (m_draggedBody)
                {
                    var md:b2MouseJointDef = new b2MouseJointDef();
                    md.body1 = m_world.GetGroundBody();
                    md.body2 = m_draggedBody;
                    md.target.Set(mouseXWorldPhys, mouseYWorldPhys);
                    md.maxForce = 3000 * m_draggedBody.GetMass();
                    md.timeStep = m_timeStep;
                    m_mouseJoint = m_world.CreateJoint(md) as b2MouseJoint;
                    m_draggedBody.WakeUp();
                }
            }

            if (!isMouseDown)
            {
                if ( m_mouseJoint )
                {
                    m_world.DestroyJoint( m_mouseJoint );
                    m_mouseJoint = null;
                }
            }

            if ( m_mouseJoint )
            {
                var p2:b2Vec2 = new b2Vec2(mouseXWorldPhys, mouseYWorldPhys);
                m_mouseJoint.SetTarget(p2);
            }
        }
        
        /**
         * Mouse Down
         * @param    event
         */
        private function mouseDownHandler(event:MouseEvent):void
        {
            isMouseDown = true;
        }

        /**
         * Mouse Up
         * @param    event
         */
        private function mouseUpHandler(event:MouseEvent):void
        {
            Mouse.cursor = MouseCursor.ARROW;
            isMouseDown = false;
            arrayIndex = -1;
        }
        
        /**
         * get number of pv3d clicked obj
         * @param    event
         */
        private function obj3dMousePressHandler(event:InteractiveScene3DEvent):void
        {
            arrayIndex = (event.target as DisplayObject3D).extra.arrayPos;
        }
        
        private function obj3dMouseOverHandler(e:InteractiveScene3DEvent):void 
        {
            Mouse.cursor = MouseCursor.HAND;
        }
        
        private function obj3dMouseOutHandler(e:InteractiveScene3DEvent):void 
        {
            if(!isMouseDown)
                Mouse.cursor = MouseCursor.ARROW;
        }
        
        private function createBackGround():void
        {
            var bgMatrix:Matrix = new Matrix();
            bgMatrix.rotate(90 * Math.PI / 180);
            graphics.beginGradientFill("linear", [0xFFFFFF, 0x001122], [100, 100], [0, 255], bgMatrix);
            graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
        }
        
        private function cerateFullScreenBtn():void
        {
            var btn:Sprite = new Sprite();
            var loader:Loader = new Loader();
            loader.load(new URLRequest("http://wonderfl.kayac.com/img/code/out_arrow_o.gif"));
            
            btn.addChild(loader);
            btn.x = 435;
            btn.y = 5;
            btn.buttonMode = true;
            
            btn.addEventListener(MouseEvent.CLICK, function():void
            {
                if (stage.displayState == StageDisplayState.FULL_SCREEN)
                    stage.displayState = StageDisplayState.NORMAL;
                else
                    stage.displayState = StageDisplayState.FULL_SCREEN;
            });
            
            addChild(btn);
        }
    }
}